#import "PhotoViewer.h"

#import "Controller.h"
#import "ExifController.h"
#import "FolderModel.h"
#import "FullScreenWindow.h"
#import "ImageView.h"
#import "KeyCodes.h"
#import "OperationData.h"
#import "PhotoBrowser.h"
#import "PreferencesData.h"
#import "ThumbData.h"
#import "ThumbnailView.h"
#import "Utility.h"


// Preference definitions
#define PREF_SORT_BY			@"Viewer.sortBy"
#define PREF_SHOW_NAME			@"Viewer.showName"
#define PREF_SHOW_DATE			@"Viewer.showDate"
#define PREF_SHOW_FILE_LENGTH	@"Viewer.showFileLength"
#define PREF_SHOW_IMAGE_SIZE	@"Viewer.showImageSize"
#define PREF_FIT_WINDOW			@"Viewer.fitWindow"
#define PREF_THUMB_STRIP_STATE	@"Viewer.thumbStripState"


//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
@implementation PhotoViewer

//------------------------------------------------------------------------------
- (void) awakeFromNib
{
	[super awakeFromNib];

	thumbViewMax = [[thumbView enclosingScrollView] frame].size.height;
	[thumbView setAcceptsFirstResponder: NO];
	[thumbView setIsAStrip: YES];
	[thumbView setVertical: NO];
	[thumbView setShowDate: NO];
	[thumbView setShowName: NO];
	[[NSNotificationCenter defaultCenter] 
		addObserver: self
		selector: @selector (thumbViewCurrentIndexChanged:)
		name: TVCurrentIndexChangedNotification
		object: thumbView];
	thumbViewState = [[[Controller sharedController] preferencesData] thumbStripState];
	[self setThumbViewState: thumbViewState];

	[imageView registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
	[thumbView registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, nil]];


	sortBy = SORT_BY_NAME;
	showName = YES;
	showDate = YES;
	showFileLength = NO;
	showImageSize = NO;
	showIndex = YES;
	fitWindow = YES;
	fullScreen = NO;


	pageSize = [[[Controller sharedController] preferencesData] pageJumpSize];

	NSUserDefaults *userPrefs = [[Controller sharedController] userPreferences];
	getBoolFromPreferences(userPrefs, PREF_SHOW_NAME, &showName);
	getBoolFromPreferences(userPrefs, PREF_SHOW_DATE, &showDate);
	getBoolFromPreferences(userPrefs, PREF_SHOW_FILE_LENGTH, &showFileLength);
	getBoolFromPreferences(userPrefs, PREF_SHOW_IMAGE_SIZE, &showImageSize);
	sortBy = getIntFromPreferences(userPrefs, PREF_SORT_BY, SORT_BY_NAME);

	[self updateColors];
}

//------------------------------------------------------------------------------
- (void) dealloc
{
	[FolderModel releaseInstance: folderModel];

	[super dealloc];
}

//------------------------------------------------------------------------------
- (void) redrawCurrent
{
	if (imageIndex >= [folderModel count])
	{
		return;
	}

	[self setImageStrings: [self currentFile]  index: imageIndex];
	[self setThumbViewState: thumbViewState];
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (void) clearSelection
{
	[thumbView clearSelection];
}

//------------------------------------------------------------------------------
- (NSString*) currentFolder
{
	if (nil == folderModel)
	{
		return nil;
	}

	return [folderModel path];
}

//------------------------------------------------------------------------------
- (FolderModel*) folderModel
{
	return folderModel;
}

//------------------------------------------------------------------------------
- (NSSize) maxThumbSize
{
	return NSMakeSize([thumbView maxThumbWidth], [thumbView maxThumbHeight]);
}

//------------------------------------------------------------------------------
- (void) redrawThumbnails:(NSArray*) items
{
	// Redraw each selected item
	int count = [items count];
	int idx;
	for (idx = 0; idx < count; ++idx)
	{
		ThumbData *td = [items objectAtIndex: idx];
		[thumbView displayThumbData: td];
	}
}

//------------------------------------------------------------------------------
- (void) selectCurrent
{
	[thumbView selectCurrent];
}

//------------------------------------------------------------------------------
- (NSMutableArray*) selectedItems
{
	return (NSMutableArray*) [thumbView selectedItems];
}

//------------------------------------------------------------------------------
- (void) thumbViewCurrentIndexChanged:(NSNotification*) note
{
	// The thumbview selection changed - we only do something if it's different than our
	// currently displayed file/image/movie.
	int newIndex = [thumbView currentIndex];
	ThumbData *tdNew = [folderModel tdAtIndex: newIndex];
	if ([curImageName isEqualToString: [tdNew name]])
	{
		return;
	}

	imageIndex = newIndex;
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
- (void) prefsDataChanged:(NSNotification*) note
{
	pageSize = [[[Controller sharedController] preferencesData] pageJumpSize];
	pathElements = [[[Controller sharedController] preferencesData] pathElements];
	[self updateColors];

	[super prefsDataChanged: note];
}

//------------------------------------------------------------------------------
- (void) updateColors
{
	NSColor *backgroundColor = [[[Controller sharedController] preferencesData] viewerBackgroundColor];

	if (nil != backgroundColor)
	{
		[imageView setBackgroundColor: backgroundColor];
		[[imageView enclosingScrollView] setBackgroundColor: backgroundColor];
		[[thumbView enclosingScrollView] setBackgroundColor: backgroundColor];
//		[movieView setFillColor: backgroundColor];
		[[imageView window] setBackgroundColor: backgroundColor];
	}
	else
	{
		[imageView setBackgroundColor: originalColor];
		[[imageView enclosingScrollView] setBackgroundColor: originalColor];
		[[thumbView enclosingScrollView] setBackgroundColor: originalColor];
//		[movieView setFillColor: originalColor];
		[[imageView window] setBackgroundColor: originalColor];
	}

	NSColor *textColor = [[[Controller sharedController] preferencesData] viewerTextColor];
	if (nil != textColor)
	{
		[imageView setForegroundColor: textColor];
		[thumbView setForegroundColor: textColor];
	}
	else
	{
		[imageView setForegroundColor: nil];
		[thumbView setForegroundColor: nil];
	}
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (IBAction) copyImage:(id)sender
{
	[super copyImage: sender];
}

//------------------------------------------------------------------------------
- (IBAction) copyImageToParent:(id)sender
{
	[super copyImageToParent: sender];
}

//------------------------------------------------------------------------------
- (IBAction) firstImage:(id)sender
{
	[NSCursor setHiddenUntilMouseMoves: YES];

	if ([folderModel count] <= 0)
	{
		return;
	}

	imageIndex = 0;
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
- (IBAction) getInfo:(id)sender
{
	[super getInfo: sender];
}

//------------------------------------------------------------------------------
- (IBAction) jumpToImage:(id)sender
{
	[NSCursor setHiddenUntilMouseMoves: YES];

	int percent = [sender tag] / 10 * 10;
	imageIndex = [folderModel count] * percent / 100;
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
- (IBAction) lastImage:(id)sender
{
	[NSCursor setHiddenUntilMouseMoves: YES];

	if ([folderModel count] <= 0)
	{
		return;
	}

	imageIndex = [folderModel count] - 1;
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
- (IBAction) moveImage:(id)sender
{
	[super moveImage: sender];
}

//------------------------------------------------------------------------------
- (IBAction) moveImageToTrash:(id)sender
{
	[super moveImageToTrash: sender];
}

//------------------------------------------------------------------------------
- (IBAction) nextImage:(id)sender
{
	[NSCursor setHiddenUntilMouseMoves: YES];

	imageIndex = [self getNextIndex: +1];
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
- (IBAction) openContainingFolder:(id)sender
{
	PhotoBrowser *pb = [[Controller sharedController] getOrCreateBrowser];
	[pb loadDirectoryAndSelect: [self currentFolder]  parentDir:nil];
}

//------------------------------------------------------------------------------
- (IBAction) previousImage:(id)sender
{
	[NSCursor setHiddenUntilMouseMoves: YES];

	imageIndex = [self getNextIndex: -1];
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
- (IBAction) refresh:(id)sender
{
	[folderModel refresh];
}

//------------------------------------------------------------------------------
- (IBAction) revealInFinder:(id)sender
{
	[[Controller sharedController] revealInFinder: [self currentFile]];
}

//------------------------------------------------------------------------------
- (IBAction) rotateRight:(id)sender
{
	[super rotateRight: sender];
}

//------------------------------------------------------------------------------
- (IBAction) rotateLeft:(id)sender
{
	[super rotateLeft: sender];
}

//------------------------------------------------------------------------------
- (IBAction) setThumbStripState:(id)sender
{
	NSMenuItem *menu = sender;
	int state = [menu tag] >> 0x04;

	[self setThumbViewState: state];
}

//------------------------------------------------------------------------------
- (IBAction) showDate:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) showFileSize:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) showImageSize:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) showName:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) sortByDates:(id)sender
{
	if (SORT_BY_DATE != sortBy)
	{
		sortBy = SORT_BY_DATE;

		ThumbData *td = [folderModel tdAtIndex: imageIndex];
		FolderModel *fm = [FolderModel instance: [self currentFolder]  sort:sortBy  stop:nil];
		[self replaceFolderModel: fm];

		imageIndex = [folderModel indexOfTD: td];
		imageIndex = [self ensureInBounds: imageIndex];
		[self showFile: imageIndex];
	}
}

//------------------------------------------------------------------------------
- (IBAction) sortByNames:(id)sender
{
	if (SORT_BY_NAME != sortBy)
	{
		sortBy = SORT_BY_NAME;

		ThumbData *td = [folderModel tdAtIndex: imageIndex];
		FolderModel *fm = [FolderModel instance: [self currentFolder]  sort:sortBy  stop:nil];
		[self replaceFolderModel: fm];

		imageIndex = [folderModel indexOfTD: td];
		imageIndex = [self ensureInBounds: imageIndex];
		[self showFile: imageIndex];
	}
}

//------------------------------------------------------------------------------
- (IBAction) sortByThumbStates:(id)sender
{
	if (SORT_BY_THUMBSTATE != sortBy)
	{
		sortBy = SORT_BY_THUMBSTATE;

		ThumbData *td = [folderModel tdAtIndex: imageIndex];
		FolderModel *fm = [FolderModel instance: [self currentFolder]  sort:sortBy  stop:nil];
		[self replaceFolderModel: fm];

		imageIndex = [folderModel indexOfTD: td];
		imageIndex = [self ensureInBounds: imageIndex];
		[self showFile: imageIndex];
	}
}

//------------------------------------------------------------------------------
- (IBAction) sortByTypes:(id)sender
{
	if (SORT_BY_TYPE != sortBy)
	{
		sortBy = SORT_BY_TYPE;

		ThumbData *td = [folderModel tdAtIndex: imageIndex];
		FolderModel *fm = [FolderModel instance: [self currentFolder]  sort:sortBy  stop:nil];
		[self replaceFolderModel: fm];

		imageIndex = [folderModel indexOfTD: td];
		imageIndex = [self ensureInBounds: imageIndex];
		[self showFile: imageIndex];
	}
}

//------------------------------------------------------------------------------
- (IBAction) toggleAutoShow:(id)sender
{
	NSString *folderName = [folderModel path];
	[[Controller sharedController] startAutoSlideshow: folderName];
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (BOOL) validateMenuItem:(id <NSMenuItem>) menuItem
{
	BOOL enabled = NO;

	[super validateMenuItem: menuItem];

	switch ([menuItem tag] & MN_TAG_MASK)
	{
		case MN_TAG_ALWAYS:
			enabled = YES;
			break;
		case MN_TAG_PATH:
			enabled = nil != folderModel;
			break;
		case MN_TAG_IMAGE:
			enabled = nil != folderModel && 0 != [folderModel count];
			break;
		case MN_TAG_JPEG:
			enabled = [self hasSelectedJPEG];
			break;
	}

	// Update the state of our 'ThumbStrip' menu items
	if ([menuItem action] == @selector(setThumbStripState:))
	{
		int state = NSOffState;
		int value = [menuItem tag] >> 0x04;

		if (value == thumbViewState)
		{
			state = NSOnState;
		}

		[menuItem setState: state];
	}

	// We want the 'view' menu disabled.
	if ([menuItem action] == @selector(openItem:))
	{
		enabled = NO;
	}

	return enabled;
}

//------------------------------------------------------------------------------
- (void) pageBackward
{
	[NSCursor setHiddenUntilMouseMoves: YES];
	imageIndex -= pageSize;
	if (imageIndex < 0)
	{
		imageIndex = [folderModel count] + imageIndex;
	}

	imageIndex = [self ensureInBounds: imageIndex];
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
- (void) pageForward
{
	[NSCursor setHiddenUntilMouseMoves: YES];
	imageIndex += pageSize;
	int extra = imageIndex - ([folderModel count]);
	if (extra > 0)
	{
		imageIndex = extra;
	}

	imageIndex = [self ensureInBounds: imageIndex];
	[self showFile: imageIndex];
}

//------------------------------------------------------------------------------
// Keystrokes our image view forwards to us
- (void) imageView:(ImageView*) imgView  keyDown:(NSEvent *) evt
{
	[NSCursor setHiddenUntilMouseMoves: YES];

	switch ([evt keyCode])
	{
		// Previous image
		case VKC_UP_ARROW:
		case VKC_LEFT_ARROW:
			[self previousImage: nil];
			break;

		// Next image
		case VKC_DOWN_ARROW:
		case VKC_RIGHT_ARROW:
			[self nextImage: nil];
			break;

		// Cancel full screen
		case VKC_ESC:
			[self cancelFullScreen: nil];
			break;

		// Toggle fullscreen
		case VKC_ENTER:
			[self toggleFullScreen: nil];
			break;

		// Backward a "page"
		case VKC_PAGE_UP:
			[self pageBackward];
			break;

		// Forward a "page"
		case VKC_PAGE_DOWN:
			[self pageForward];
			break;

		// Last image
		case VKC_END:
			[self lastImage: nil];
			break;

		// First image
		case VKC_HOME:
			[self firstImage: nil];
			break;

		default:
			NSBeep();
			break;
	}
}

//------------------------------------------------------------------------------
- (int) indexByName:(NSString *) name
{
	if (nil == name)
	{
		return -1;
	}

	int count = [folderModel count];
	int idx;
	for (idx = 0; idx < count; ++idx)
	{
		ThumbData *td = [folderModel tdAtIndex: idx];
		if (NSOrderedSame == [name compare: [td name]])
		{
			return idx;
		}
	}

	return -1;
}

//------------------------------------------------------------------------------
- (void) setImageStrings:(NSString *) fullFilename index:(int) index
{
	NSDate *date = [[folderModel tdAtIndex: index] date];
	[super setImageStrings
		: fullFilename  
		index:index + 1  
		totalCount:[folderModel count]  
		date:date];
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (void) folderModelItemsAdded:(NSNotification *) note
{
	// Our index may have changed; determine the new index and update the strings
	// (don't update the image, it hasn't changed and shouldn't be redrawn).
	int newIndex = [self indexByName: curImageName];
	newIndex = [self ensureInBounds: newIndex];

	if (newIndex != imageIndex)
	{
		imageIndex = newIndex;
		ThumbData *td = [folderModel tdAtIndex: imageIndex];
		[self setImageStrings :[td fullPath]  index:imageIndex];

		[curImageName release];
		curImageName = [td name];
		[curImageName retain];
	}
}

//------------------------------------------------------------------------------
- (void) folderModelItemsRemoved:(NSNotification *) note
{
	// Our index may have changed OR the current image may have been removed.
	// If the index changed, update the strings. Otherwise, choose another
	// image (hopefully at the same index).
	int newIndex = [self indexByName: curImageName];
	ThumbData *td = [folderModel tdAtIndex: imageIndex];

	if (newIndex != imageIndex || NO == [[td name] isEqualToString: curImageName])
	{
		if (-1 != newIndex)
		{
			imageIndex = newIndex;
		}

		imageIndex = [self ensureInBounds: imageIndex];
		
		if (newIndex == imageIndex)
		{
			td = [folderModel tdAtIndex: imageIndex];
			[curImageName release];
			curImageName = [td name];
			[curImageName retain];
		}
		else
		{
			// reload, redraw
			[self showFile: imageIndex];
		}
	}
}

//------------------------------------------------------------------------------
- (void) folderModelItemsUpdated:(NSNotification *) note
{
	if (nil == curImageName)
	{
		return;
	}

	// If the current item was updated, reload it.
	NSArray *thumbArray = [[note userInfo] objectForKey: FM_NOTIFY_ITEMS];
	int idx;
	for (idx = 0; idx < [thumbArray count]; ++idx)
	{
		ThumbData *td = [thumbArray objectAtIndex: idx];
		if (NSOrderedSame == [curImageName compare: [td name]])
		{
			// reload, redraw
			[self showFile: imageIndex];
			break;
		}
	}
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (NSString*) currentFile
{
	return [[folderModel tdAtIndex: imageIndex] fullPath];
}

//------------------------------------------------------------------------------
- (int) sortBy
{
	return sortBy;
}

//------------------------------------------------------------------------------
- (BOOL) hasDirectory
{
	return NO;
}

//------------------------------------------------------------------------------
- (BOOL) hasSelectedImage
{
	return [folderModel count] > 0;
}

//------------------------------------------------------------------------------
- (BOOL) hasSelectedJPEG
{
	if ([folderModel count] < 1)
	{
		return NO;
	}

	ThumbData *td = [folderModel tdAtIndex: imageIndex];
	return  nil != td && FILE_TYPE_JPEG == [td fileType];
}

//------------------------------------------------------------------------------
- (void) openImageFile:(NSString *) path  showImage:(BOOL) showImage
{
	BOOL isDir;
	if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir])
	{
		FMStartLoad *fmLoad = [[FMStartLoad alloc] initFile:path  showImage:showImage];
		[NSThread detachNewThreadSelector
			: @selector(loadFolderModel:)
			toTarget: self
			withObject: fmLoad];


		// If we're opening an image, show it right away.
		if (NO == isDir)
		{
			if (MEDIA_TYPE_MOVIE == [Controller mediaTypeForFile: path])
			{
				[self displayMovie: path];
			}
			else
			{
				[self displayImage: path];
			}
		}
	}
}

//------------------------------------------------------------------------------
- (void) loadFolderModel:(FMStartLoad *) fmLoad
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];


	NSString *path = [fmLoad path];

	BOOL isDir;
	[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];

	// If it's a file, get the starting filename and directory.
	NSString *firstFile = nil;
	if (NO == isDir)
	{
		firstFile = [path lastPathComponent];
		path = [path stringByDeletingLastPathComponent];
	}

	// Get the folder list, filtered & sorted.
	FolderModel *fm = [FolderModel instance: path  sort:sortBy  stop:nil];

	BOOL showImage = [fmLoad showImage];
	FMAvailableData *fmData = [[FMAvailableData alloc] initFile: firstFile  fm:fm  showImage:showImage];
	[self performSelectorOnMainThread
			: @selector(folderModelAvailable:)
			withObject: fmData
			waitUntilDone: NO];
	[fmData release];


	[pool release];
}

//------------------------------------------------------------------------------
- (void) folderModelAvailable:(FMAvailableData*) fmData
{
	[self replaceFolderModel: [fmData folderModel]];

	imageIndex = 0;
	NSString *firstFile = [fmData firstFile];
	if (nil != firstFile)
	{
		int idx = [self indexByName: firstFile];
		if (-1 != idx)
		{
			imageIndex = idx;
		}
	}

	if (YES == [fmData showImage])
	{
		[self showFile: imageIndex];
	}
}

//------------------------------------------------------------------------------
- (void) replaceFolderModel:(FolderModel *) fm
{
	if (nil != folderModel)
	{
		[[NSNotificationCenter defaultCenter] removeObserver :self  name:nil  object:folderModel];
		[FolderModel releaseInstance: folderModel];
	}

	folderModel = fm;
	if (nil != folderModel)
	{
		[[NSNotificationCenter defaultCenter] addObserver
			: self
			selector: @selector (folderModelItemsAdded:)
			name: FMItemsAddedNotification
			object: folderModel];

		[[NSNotificationCenter defaultCenter] addObserver
			: self
			selector: @selector (folderModelItemsRemoved:)
			name: FMItemsRemovedNotification
			object: folderModel];

		[[NSNotificationCenter defaultCenter] addObserver
			: self
			selector: @selector (folderModelItemsUpdated:)
			name: FMItemsUpdatedNotification
			object: folderModel];
	}

	[thumbView setFolderModel: fm  addThumbs: NO];
}

//------------------------------------------------------------------------------
- (BOOL) showFile:(int) index
{
	BOOL ret = NO;
	if (nil != folderModel && index >= 0 && index < [folderModel count])
	{
		ThumbData *td = [folderModel tdAtIndex: index];

		// Keep track of the image name for notifications
		[curImageName release];
		curImageName = [td name];
		[curImageName retain];

		ret = [self displayFile: td];

		if (YES == ret)
		{
			[thumbView setCurrent: index];
			[self setImageStrings: [td fullPath]  index:index];
		}

		[self selectionChanged: td];
	}

	return ret;
}

//------------------------------------------------------------------------------
- (int) getNextIndex:(int) diff
{
	if (nil == folderModel)
	{
		return 0;
	}

	int index = imageIndex + diff;
	index = [self ensureInBounds: index];

	return index;
}

//------------------------------------------------------------------------------
- (int) ensureInBounds:(int) index
{
	if (index < 0)
	{
		index = [folderModel count] - 1;
		if (index < 0)
		{
			index = 0;
		}
	}
	else
	if (index >= [folderModel count])
	{
		index = 0;
	}

	return index;
}

//------------------------------------------------------------------------------
- (void) setThumbViewState:(int) newState
{
	[super setThumbViewState: newState];
	if (TVS_HIDDEN != thumbViewState)
	{
		[thumbView setCurrent: imageIndex];
	}
}

//------------------------------------------------------------------------------
- (void) windowWillClose:(NSNotification*) notification
{
	[self endFullScreenDuringClose];

	// Cancel any movie currently playing.
//	[movieView pause: nil];
	[movieView setMovie: nil];

	NSUserDefaults *userPrefs = [[Controller sharedController] userPreferences];
	[userPrefs setBool: showName forKey: PREF_SHOW_NAME];
	[userPrefs setBool: showDate forKey: PREF_SHOW_DATE];
	[userPrefs setBool: showFileLength forKey: PREF_SHOW_FILE_LENGTH];
	[userPrefs setBool: showImageSize forKey: PREF_SHOW_IMAGE_SIZE];
	[userPrefs setBool: fitWindow forKey: PREF_FIT_WINDOW];

	[userPrefs setInteger:sortBy  forKey: PREF_SORT_BY];

	NSRect frame = [[imageView window] frame];
	[userPrefs setObject: NSStringFromRect(frame)  forKey: PREF_VIEWER_FRAME_RECT];

	[[Controller sharedController] viewerClosing: self];
}

@end
